package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    public Map<Long, List<Long>> treeMapA = new ConcurrentHashMap<>();
    public Map<Long, List<Long>> treeMapB = new ConcurrentHashMap<>();
    public Map<Long, List<Long>> treeMapC = new ConcurrentHashMap<>();

    public TPMonitorFilter() {
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
            Date date = new Date();
            SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            String dateStr = sdf.format(date);
            System.out.println("打印时间:" + dateStr);
            print(Collections.unmodifiableMap(treeMapA));
            print(Collections.unmodifiableMap(treeMapB));
            print(Collections.unmodifiableMap(treeMapC));
        }, 0, 5, TimeUnit.SECONDS);

        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
            Date date = new Date();
            SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            String dateStr = sdf.format(date);
            clearTimeoutData(treeMapA);
            clearTimeoutData(treeMapB);
            clearTimeoutData(treeMapC);
        }, 60, 60, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        try {
            // 执行方法
            return  invoker.invoke(invocation);
        } finally {
            Long currentTime = System.currentTimeMillis();
            Long costTime = currentTime - startTime;
            String methodName = invocation.getMethodName();
            if(methodName.equals("methodA")) {
                record(treeMapA, startTime, costTime);
            } else if(methodName.equals("methodB")) {
                record(treeMapB, startTime, costTime);
            } else if(methodName.equals("methodC")) {
                record(treeMapC, startTime, costTime);
            }
        }

    }

    private void record(Map<Long, List<Long>> treeMap, Long startTime, Long costTime) {
        if(!treeMap.containsKey(startTime)) {
            List<Long> valueList = new CopyOnWriteArrayList<>();
            valueList.add(costTime);
            treeMap.put(startTime, valueList);
        } else {
            treeMap.get(startTime).add(costTime);
        }
    }

    private void clearTimeoutData(Map<Long, List<Long>> map) {
        Long timeoutTime = System.currentTimeMillis() - 60000;
        Iterator<Map.Entry<Long, List<Long>>> iterator = map.entrySet().iterator();
        while(iterator.hasNext()) {
            Map.Entry<Long, List<Long>> next = iterator.next();
            if(next.getKey() < timeoutTime) {
                iterator.remove();
            }
        }
    }

    private void print(Map<Long, List<Long>> map) {
        Long currentTime = System.currentTimeMillis();
        Long startTime = currentTime - 60000;
        List<Long> valueList = new ArrayList<>();
        for(Map.Entry<Long, List<Long>> entry : map.entrySet()) {
            if(entry.getKey() >= startTime) {
                valueList.addAll(entry.getValue());
            }
        }

        Collections.sort(valueList);
        int tp90Idx = (int) (valueList.size()  * 0.9);
        int tp99Idx = (int) (valueList.size() * 0.99);
        System.out.println("size:" + valueList.size() +
                        "  tp90Idx:" + tp90Idx +
                        "  tp99Idx:" + tp99Idx +
                "  tp90:" + valueList.get(tp90Idx) + "ms, tp99:" + valueList.get(tp99Idx) + "ms");
    }
}
